package autoActivity;


import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.logging.Level;

import javax.imageio.ImageIO;
import javax.net.ssl.HttpsURLConnection;

import org.apache.http.Header;
import org.apache.http.client.CookieStore;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.BasicCookieStore;
import org.json.JSONArray;
import org.json.JSONObject;

import com.arronlong.httpclientutil.HttpClientUtil;
import com.arronlong.httpclientutil.common.HttpConfig;
import com.arronlong.httpclientutil.common.HttpHeader;
import com.arronlong.httpclientutil.exception.HttpProcessException;
import com.machinepublishers.jbrowserdriver.JBrowserDriver;
import com.machinepublishers.jbrowserdriver.Settings;
import com.machinepublishers.jbrowserdriver.Timezone;

import autoActivity.tools.CodeIdentification;
import autoActivity.tools.LoadPropertiesUtil;
import autoActivity.tools.MailUtils;


public class Main {

	//会话信息
	private static String loginurl = "https://gmis.buct.edu.cn/home/stulogin";
	private static JBrowserDriver driver = new JBrowserDriver(Settings.builder().javascript(true).ignoreDialogs(false).timezone(Timezone.AMERICA_NEWYORK).headless(true).loggerLevel(Level.OFF).build());
	private static String MYSINDEXCOOKIE = "";//主机密钥
	private static String spwd = "";//url中的密钥
	//个人信息
	private static String userid = LoadPropertiesUtil.getUserId();
	private static String passwd = LoadPropertiesUtil.getUserPass();
	private static String email = LoadPropertiesUtil.getEmail();//接受报告的邮件
	//系统信息
	private static int maxLogin = 5;//最大登录次数
	private static int interval = LoadPropertiesUtil.getInterval();//轮询间隔默认1秒
	private static boolean token = false;//token是否失效

	public static void main(String[] args) {
		int errorNum = 0;
		boolean first = true;
		try {
			//token失效重新登录
			while(!token) {
				if(!first) System.out.println("重新登录中......");
				if(login(0)) {
					token = true;
					if(first) {
						System.out.println("=====================================");
						getMyInfo();
						first = false;
					}
					System.out.println("正在监听中...");
					//允许有一次出错机会，例如验证码错误或登录失效，超过2次则重新登录
					while(errorNum<2&&token) {
						List<JSONObject> list = getActivities();
						errorNum = applyActivities(list);
						Thread.sleep(interval*1000);//轮询活动间隔默认为1秒
					}
					System.out.println("监听异常！");
				}else {
					System.out.println("登录失败，可能敌人奔溃了！");
					break;
				}
			}
		} catch (Exception e) {
			System.out.println("登录异常！");
		}
		driver.quit();

	}

	/**
	 * 登录账号
	 * @param loginCount
	 * @return 返回登录状态，最大尝试maxLogin次登录
	 */
	public static boolean login(int loginCount) {
		try {
			//输入地址并访问，自动进行重定向
			driver.get(loginurl);
			loginurl = driver.getCurrentUrl();//重定向后最终的地址，包含密钥
			//获取地址中的密钥
			int start = loginurl.indexOf("/(S(")+4;
			int end = loginurl.indexOf("))/");
			spwd = loginurl.substring(start,end);

			//获取RSA加密插件，对明文密码加密
			String s = driver.executeScript("var p = '"+passwd+"';var rsa = new JSEncrypt();var pubkey = $(\"#pubkey\").val();rsa.setPublicKey(pubkey);return rsa.encrypt(p)").toString();

			//验证码解析
			String codepath = "https://gmis.buct.edu.cn/(S("+spwd+"))/Home/VerificationCode?codetype=stucode&&t="+System.currentTimeMillis();
			BufferedImage img = getUrlImage(codepath);
			String codeResult = CodeIdentification.getCodeFromBufferedImage(img);//验证码解析结果

			//构造用户登录信息
			String path = "https://gmis.buct.edu.cn/(S("+spwd+"))/home/stulogin_do";
			String requestBody = "{\"UserId\":\""+userid+"\",\"Password\":\""+s
					+"\",\"VeriCode\":\""+codeResult+"\",\"url\":\"\",\"city\":\"\"}";

			//post请求后端接口
			String postResult = postInfo(path,requestBody,MYSINDEXCOOKIE);
			System.out.println(postResult);
			if(postResult.contains("student/default/index")) {
				return true;
			}

		}catch(Exception e) {
			if(loginCount<maxLogin) return login(loginCount+1);
		}
		return false;
	}

	/**
	 * 获取个人信息
	 */
	public static void getMyInfo() {
		Scanner input = new Scanner(System.in);
		try {
			String myInfo = postInfo("https://gmis.buct.edu.cn/(S("+spwd+"))/student/default/getxscardinfo","",MYSINDEXCOOKIE);
			JSONArray jsArray = new JSONArray(myInfo);
			JSONObject jObject = jsArray.getJSONObject(0);
			System.out.println("姓名："+jObject.getString("xm"));
			System.out.println("学号："+jObject.getString("xh"));
			System.out.println("学院："+jObject.getString("xsmc"));
		} catch (Exception e) {
			System.out.println("姓名：null");
			System.out.println("学号：null");
			System.out.println("学院：null");
		}
		System.out.println("信息正确按任意键继续，否则Ctrl+C中断重新运行。");
		input.nextLine();
	}

	/**
	 * 获取所有可报名的活动
	 * @return 返回可报名的活动id
	 */
	public static List<JSONObject> getActivities() {
		List<JSONObject> list = new ArrayList<>();
		try {
			String activityInfos = postInfo("https://gmis.buct.edu.cn/(S("+spwd+"))/student/yggl/xshdbm_sqlist","",MYSINDEXCOOKIE);
			JSONObject jObject = new JSONObject(activityInfos);
			JSONArray jArray = jObject.getJSONArray("rows");
			for(int i=0;i<jArray.length();i++) {
				jObject = jArray.getJSONObject(i);
				int rs = jObject.getInt("rs");//活动限定人数
				int bmrs = jObject.getInt("bmrs");//当前报名人数
				//System.out.println("【活动名称】："+jObject.getString("mc")+"；【开始时间】："+jObject.getString("kssj")+"；【结束时间】："+jObject.getString("jzsj"));
				if(bmrs<rs) {//有名额
					list.add(jObject);
				}
			}
		} catch (Exception e) {
			token = false;
		}
		return list;
	}

	/**
	 * 根据活动id进行报名
	 * @param list
	 * @return int 返回确认报名失败次数，失败可能是账号失效，网站崩溃，验证码识别错误
	 */
	public static int applyActivities(List<JSONObject> list){
		int n = 0;
		for (JSONObject jObject : list) {
			int id = jObject.getInt("id");//活动id
			String code = getCodeImage();//验证码
			String httpurl = "https://gmis.buct.edu.cn/(S("+spwd+"))/student/yggl/xshdbm_bm?id="+id+"&VeriCode="+code;
			//提交报名信息
			try {
				String result = postInfo(httpurl,"",MYSINDEXCOOKIE);//成功返回{"zt":"1"}
				//JSONObject obj=new JSONObject(result);
				//新版网站bug不管成功失败都返回{"zt":"0"}
				//if(obj.getString("zt").equals("1")) {
				String mc = jObject.getString("mc");
				String kssj = jObject.getString("kssj");
				String jzsj = jObject.getString("jzsj");
				System.out.println("北化程序强提醒您：活动报告【"+mc+"】报名成功啦！");
				if(!email.isEmpty()) MailUtils.sendMail(email,"【活动报告报名提醒】",
						"北化程序强提醒您，您申请的活动报告【"+mc+"】报名成功！"+System.getProperty("line.separator")+
								"【活动开始时间】："+kssj+System.getProperty("line.separator")+
								"【活动结束时间】："+jzsj+System.getProperty("line.separator")+
								"请及时登录平台查看!"
				);
				//}
			} catch (Exception e) {
				System.out.println("异常");
				n++;
			}
		}
		return n;
	}

	/**
	 * 获取报名验证码
	 * @return 返回验证码解析的字符串
	 */
	public static String getCodeImage() {
		try {
			String codeImgUrl = "https://gmis.buct.edu.cn/(S("+spwd+"))/student/yggl/VerificationCode";
			BufferedImage img = getUrlImage(codeImgUrl);
			String codeResult = CodeIdentification.getCodeFromBufferedImage(img);
			if(codeResult.isEmpty()) System.out.println("报名验证码为空！");
			else return codeResult;
		} catch (Exception e) {
		}
		return "0000";
	}

	/**
	 * 根据验证码的图片地址解析为BufferedImage流
	 * @param httpurl
	 * @return
	 */
	public static BufferedImage getUrlImage(String httpurl) {
		BufferedImage img = null;
		try {
			String cookies = "__SINDEXCOOKIE__="+MYSINDEXCOOKIE;
			URL url = new URL(httpurl);
			HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
			conn.setRequestProperty("Cookie",cookies);
			conn.setRequestMethod("GET");
			conn.setDoInput(true);
			conn.setDoOutput(true);
			img = ImageIO.read(conn.getInputStream());
		} catch (IOException e) {
			System.out.println("报名验证码获取异常！");
			e.printStackTrace();
		}
		return img;
	}

	/**
	 * 获取url中的密钥，即S(xxx)中的内容
	 * @param path
	 * @return
	 * @throws HttpProcessException
	 */
	public static String getSPWD(String path) throws HttpProcessException {
		HttpConfig config = HttpConfig.custom().url(path).encoding("utf-8");
		String result = HttpClientUtil.post(config);
		int start = result.indexOf("/(S(")+4;
		int end = result.indexOf("))/");
		result = result.substring(start,end);
		return result;
	}

	/**
	 * post请求，具体使用参考：https://github.com/Arronlong/httpclientutil
	 * @param path
	 * @param requestBody
	 * @param SINDEXCOOKIE
	 * @return
	 * @throws HttpProcessException
	 */
	public static String postInfo(String path, String requestBody,String SINDEXCOOKIE) throws HttpProcessException {
		//配置cookie
		HttpClientContext context = new HttpClientContext();
		CookieStore cookieStore = new BasicCookieStore();
		context.setCookieStore(cookieStore);
		//配置头文件
		Header[] headers = HttpHeader.custom()
				.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36 Edg/94.0.992.47")
				.host("gmis.buct.edu.cn")
				.connection("keep-alive")
				.contentType("application/x-www-form-urlencoded;charset=UTF-8")
				.accept("application/json, text/javascript, */*; q=0.01")
				.acceptLanguage("zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
				.referer("http://gmis.buct.edu.cn/home/stulogin")
				.other("X-Requested-With", "XMLHttpRequest")
				.other("Origin", "http://gmis.buct.edu.cn")
				.cookie("__SINDEXCOOKIE__="+SINDEXCOOKIE+"; path=/")
				.build();

		//配置待发送的参数
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("json", requestBody);

		//插件式配置请求参数（网址、请求参数、编码、client）
		HttpConfig config = HttpConfig.custom().headers(headers).url(path).map(map).context(context).encoding("utf-8");
		//post请求并返回结果
		String result = HttpClientUtil.post(config);
		//获取cookie
		cookieStore = context.getCookieStore();
		for (org.apache.http.cookie.Cookie cookie : cookieStore.getCookies()) {
			if(cookie.getName().equals("__SINDEXCOOKIE__")) {
				MYSINDEXCOOKIE = cookie.getValue();
				break;
			}
		}

		return result;
	}

}
